﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using net.entity.framework.common;

namespace net.entity.framework.db {
    internal class TempTable : IDisposable {
        #region Members

        private bool isCreated;
        private readonly DataTable dataSource;
        private readonly string sourceTableName;
        private readonly string destinationTableName;
        private readonly List<string> columnNames;
        private readonly DataProvider provider;
        private readonly DbClient client;

        #endregion

        #region Constructors

        internal TempTable(DataTable dataSource, DataProvider provider) {
            sourceTableName = "#temp" + dataSource.TableName;
            destinationTableName = dataSource.TableName;
            this.dataSource = dataSource;
            this.provider = provider;
            foreach (DataColumn column in dataSource.Columns) {
                columnNames.Add(column.ColumnName);
            }
        }

        #endregion

        #region Methods

        internal void UpdateDestination() {
            CreateTemp();
            CopyFrom(dataSource);
            var commandBuilder = new StringBuilder();
            GetUpdateCommand(commandBuilder);
            GetDropTempCommand(commandBuilder);
            Repository.ExecuteNonQuery(provider.GetConnection(false),
                                       provider.GetTransaction(false),
                                       commandBuilder.ToString());
            isCreated = false;
        }

        private void CreateTemp() {
            if (isCreated) {
                throw new Exception(String.Format("'{0}' is already created.", destinationTableName));
            }
            var commandText = String.Format("SELECT *, [RowState]=0 INTO {0} FROM {1} WHERE 1=0",
                                            destinationTableName, sourceTableName);
            Repository.ExecuteNonQuery(provider.GetConnection(false), provider.GetTransaction(false), commandText);
            isCreated = true;
        }


        private void CopyFrom(DataTable sourceDataTable) {
            var bulkCopy = DbBulkCopy.Create(provider);
            bulkCopy.DestinationTableName = destinationTableName;
            columnNames.ForEach(x => bulkCopy.ColumnMappings.Add(x, x));
            bulkCopy.WriteToServer(sourceDataTable);
        }

        private void GetUpdateCommand(StringBuilder commandBuilder) {
            //Audit trail for Update/Delete
            commandBuilder.AppendFormat(
                @"UPDATE t0 
SET t0.[revision] = t1.[revision] + 1
FROM {0} t0
    JOIN {0} t1 ON t0.[id] = t1.[id] 
    JOIN {1} t2 ON t0.[id] = t2.[id] 
        --AND t0.[timeStamp] = t2.[timeStamp] 
        AND t2.[rowState] IN (1,2)
WHERE t1.[revision] = (SELECT MAX(revision) FROM {0} WHERE id = t0.[id])",
                destinationTableName, sourceTableName);

            commandBuilder.AppendLine();

            //Add items for Insert/Update/Delete
            commandBuilder.AppendFormat(
                "INSERT INTO {0} ({2}) SELECT {3} FROM {1} t0",
                destinationTableName, sourceTableName,
                Coding.MakeCSV(columnNames, "[", "]", "RowState"),
                Coding.MakeCSV(columnNames, "t0.[", "]", "RowState"));
        }

        private void GetDropTempCommand(StringBuilder commandBuilder) {
            commandBuilder.AppendFormat(@"
--IF EXIST 
    DROP TABLE {0}
--GO", destinationTableName);
        }

        private void DropTemp() {
            if (isCreated) {
                var commandBuilder = new StringBuilder();
                GetDropTempCommand(commandBuilder);
                Repository.ExecuteNonQuery(provider, commandBuilder.ToString());
                isCreated = false;
            }
        }

        public void Dispose() {
            DropTemp();
        }

        #endregion
    }
}